/*-------------------------------------------------------------------------
 *
 * slru.h
 *	 简单的 LRU 缓冲以用于事务状态日志文件
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/slru.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SLRU_H
#define SLRU_H

#include "access/xlogdefs.h"
#include "storage/lwlock.h"
#include "storage/sync.h"


/*
 * 定义 SLRU 段大小。 页面大小与 Postgres 中其他地方使用的 BLCKSZ 相同。
 * 段大小可以选择得有些任意；我们默认将其设置为 32 页面，或者 256Kb，即对于 CLOG
 * 1M 事务或对于 SUBTRANS 64K 事务。
 *
 * 注意：由于 TransactionIds 是 32 位，并在 0xFFFFFFFF 处环绕，
 * 页面编号也在 0xFFFFFFFF/xxxx_XACTS_PER_PAGE（其中 xxxx 分别是 CLOG 或 SUBTRANS）处环绕，
 * 段编号在 0xFFFFFFFF/xxxx_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT 处环绕。我们
 * 在 slru.c 中无需特别注意该事实，除了在 SimpleLruTruncate 中比较
 * 段和页面编号时（参见 PagePrecedes()）。
 */
#define SLRU_PAGES_PER_SEGMENT	32

/*
 * 页面状态代码。 注意，这些不包括 "脏" 位。
 * page_dirty 只会在 VALID 或 WRITE_IN_PROGRESS 状态下为真；
 * 在后者情况下，意味着页面在写入开始后已被重新标记为脏。
 */
typedef enum
{
	SLRU_PAGE_EMPTY,			/* 缓冲区未使用 */
	SLRU_PAGE_READ_IN_PROGRESS, /* 页面正在读取中 */
	SLRU_PAGE_VALID,			/* 页面有效且未被写入 */
	SLRU_PAGE_WRITE_IN_PROGRESS /* 页面正在写出 */
} SlruPageStatus;

/*
 * 共享内存状态
 */
typedef struct SlruSharedData
{
	LWLock	   *ControlLock;

	/* 该 SLRU 结构管理的缓冲区数量 */
	int			num_slots;

	/*
	 * 持有每个缓冲区插槽信息的数组。 当状态为 EMPTY 时，页面编号未定义，
	 * page_lru_count 也是如此。
	 */
	char	  **page_buffer;
	SlruPageStatus *page_status;
	bool	   *page_dirty;
	int		   *page_number;
	int		   *page_lru_count;
	LWLockPadded *buffer_locks;

	/*
	 * 与 SLRU 页面中条目相关的 WAL 刷新 LSN 的可选数组。
	 * 如果不为零/NULL，则在写入页面之前必须刷新 WAL（对 pg_xact 为真，
	 * 对 multixact, pg_subtrans, pg_notify 为假）。 group_lsn[]
	 * 每个缓冲区插槽有 lsn_groups_per_page 条目，每个条目包含
	 * 对于该插槽页面的连续 SLRU 条目已知的最高 LSN。
	 */
	XLogRecPtr *group_lsn;
	int			lsn_groups_per_page;

	/*----------
	 * 我们通过设置
	 *		page_lru_count[slotno] = ++cur_lru_count;
	 * 将页面标记为“最近使用”。 因此，最旧的页面是具有最高值的
	 *		cur_lru_count - page_lru_count[slotno]
	 * 计数最终会循环，但只要没有页面的年龄超过 INT_MAX 计数，这个计算仍然
	 * 有效。
	 *----------
	 */
	int			cur_lru_count;

	/*
	 * latest_page_number 是日志当前结束的页面编号；
	 * 这不是关键数据，因为我们仅用它来避免将最新页面换出。
	 */
	int			latest_page_number;

	/* SLRU 的索引用于统计目的（可能不是唯一的） */
	int			slru_stats_idx;
} SlruSharedData;

typedef SlruSharedData *SlruShared;


/*
 * SlruCtlData 是一个未共享的结构，指向共享内存中的活动信息。
 */
typedef struct SlruCtlData
{
	SlruShared	shared;

	/*
	 * 在将同步请求交给检查点程序时使用哪个同步处理程序函数。 SYNC_HANDLER_NONE 可禁用 fsync（例如 pg_notify）。
	 */
	SyncRequestHandler sync_handler;

	/*
	 * 决定一个页面是否对于截断是“更旧”的，并作为
	 * 驱逐页面的LRU顺序的提示。如果第一个
	 * 参数的每个条目都比第二个参数的每个条目更旧，则返回true。请注意
	 * !PagePrecedes(a,b) && !PagePrecedes(b,a) 并不一定意味着 a==b; 当某些条目更旧而某些条目不是时，也会出现这种情况。对于使用
	 * SimpleLruTruncate() 的SLRUs，这必须使用模算术。(对于其他，
	 * 此回调的行为没有功能影响。) 在满足其标准的SLRUs中使用
	 * SlruPagePrecedesUnitTests()。
	 */
	bool		(*PagePrecedes) (int, int);

	/*
	 * Dir在SimpleLruInit期间设置，之后不会更改。由于
	 * 它始终相同，因此不需要将其放入共享内存中。
	 */
	char		Dir[64];
} SlruCtlData;

typedef SlruCtlData *SlruCtl;


extern Size SimpleLruShmemSize(int nslots, int nlsns);
extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
						  LWLock *ctllock, const char *subdir, int tranche_id,
						  SyncRequestHandler sync_handler);
extern int	SimpleLruZeroPage(SlruCtl ctl, int pageno);
extern int	SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
							  TransactionId xid);
extern int	SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno,
									   TransactionId xid);
extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
extern void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied);
#ifdef USE_ASSERT_CHECKING
extern void SlruPagePrecedesUnitTests(SlruCtl ctl, int per_page);
#else
#define SlruPagePrecedesUnitTests(ctl, per_page) do {} while (0)
#endif
extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);
extern bool SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno);

typedef bool (*SlruScanCallback) (SlruCtl ctl, char *filename, int segpage,
								  void *data);
extern bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data);
extern void SlruDeleteSegment(SlruCtl ctl, int segno);

extern int	SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path);

/* SlruScanDirectory公共回调 */
extern bool SlruScanDirCbReportPresence(SlruCtl ctl, char *filename,
										int segpage, void *data);
extern bool SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage,
								   void *data);

#endif							/* SLRU_H */
